/*
 * =====================================================================================
 *
 *       Filename:  cl_tcp_client.cxx
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  04/28/2011 08:07:40 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Chen Ken (Ken), zlchen.ken@gmail.com
 *        Company:  EMC
 *
 * =====================================================================================
 */

#ifndef __GXX_EXPERIMENTAL_CXX0X__
#define __GXX_EXPERIMENTAL_CXX0X__
#endif

#include "cl_tcp_client.hxx"
#include "cl_common_defs.hxx"
#include "cl_event_looper.hxx"
#include "cl_channel.hxx"
#include "cl_client_socket.hxx"
#include "cl_logger.hxx"

namespace neo
{
using namespace std;
using namespace std::placeholders;

CLTcpClient::CLTcpClient(const string& server, int port) 
	: m_ioLooper(new CLEventLooper),
	m_connector(new CLClientSocket(m_ioLooper.get(), server, port, CL_SOCK_STREAM)), 
	m_retry(false),
  m_state(TCP_CONN_DISCONNECTED),
	m_nextConnId(0)
{
  m_connector->setNewConnCallback(bind(&CLTcpClient::handleNewConnection, this, _1, _2));
  m_connector->setFailConnCallback(bind(&CLTcpClient::handleFailConnection, this));
  m_ioLooper->start();
}

CLTcpClient::~CLTcpClient() 
{
}

void CLTcpClient::connect(void) 
{
  m_connector->connect(); 
	setState(TCP_CONN_CONNECTING);
}

void CLTcpClient::disconnect(void)
{
  assert(m_connection.get() != NULL);
  m_connection->shutDown();
  setState(TCP_CONN_DISCONNECTED);
}

void CLTcpClient::enableRetry(void) 
{
	m_retry = true;
}

void CLTcpClient::setConnCallback(const CLConnCallback& cb) 
{
	m_connCallback = cb;
}

void CLTcpClient::setMessageCallback(const CLMessageCallback& cb) 
{
	m_msgCallback = cb;
}

void CLTcpClient::setWriteCompleteCallback(const CLWriteCompleteCallback& cb) 
{
	m_writeCompleteCallback = cb;
}

void CLTcpClient::handleNewConnection(int connfd, CLSocketAddrUPtr serverAddr) 
{
  CLLOG_D("NET", "CLTcpClient:: got a new connection %s", serverAddr->toString().c_str());
  CLTcpConnSPtr conn(new CLTcpConn(m_ioLooper.get(), m_nextConnId++, connfd, move(serverAddr)));
  conn->setConnCallback(m_connCallback);
	conn->setMessageCallback(m_msgCallback);
	conn->setWriteCompleteCallback(m_writeCompleteCallback);
  conn->setCloseCallback(bind(&CLTcpClient::removeConnection, this, _1));
  conn->handleEstablished();
	m_connection = conn;
	setState(TCP_CONN_CONNECTED);
}

void CLTcpClient::handleFailConnection(void)
{
	setState(TCP_CONN_DISCONNECTED);
}

void CLTcpClient::removeConnection(const CLTcpConnSPtr& conn)
{
  m_connection.reset();
  m_ioLooper->queueJob(bind(&CLTcpConn::handleDestroy, conn));
  if(m_retry && (m_state != TCP_CONN_DISCONNECTED))
	{
	  m_connector->reconnect();
	}
}

void CLTcpClient::setState(int state)
{
	m_state = state;
}

} // neo

